package main

import (
	"context"
	student_service "dqq/micro_service/grpc"
	"fmt"
	"io"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 1000*time.Millisecond) //连接超时设置为1000毫秒
	defer cancel()
	//连接到服务端
	conn, err := grpc.DialContext(
		ctx,
		"127.0.0.1:5678",
		grpc.WithTransportCredentials(insecure.NewCredentials()), //Credential即使为空，也必须设置
		grpc.WithBlock(), //grpc.WithBlock()直到连接真正建立才会返回，否则连接是异步建立的。因此grpc.WithBlock()和Timeout结合使用才有意义。server端正常的情况下使用grpc.WithBlock()得到的connection.GetState()为READY，不使用grpc.WithBlock()得到的connection.GetState()为IDEL
		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(10<<20), grpc.MaxCallRecvMsgSize(10<<20)), //默认情况下SendMsg上限是MaxInt32，RecvMsg上限是4M，这里都修改为10M
	)
	if err != nil {
		fmt.Printf("dial failed: %s", err)
		return
	}
	//创建client
	client := student_service.NewStudentServiceClient(conn)

	//准备好请求参数
	request := student_service.StudentID{Id: 10}
	//发送请求，取得响应
	response, err := client.GetStudent(context.Background(), &request)
	if err != nil {
		fmt.Printf("get student failed: %s", err)
	} else {
		fmt.Println(response.Id)
	}
	fmt.Println()

	request2 := student_service.StudentIDs{Ids: []int32{}} // 参数为空，故意触发服务端返回error
	response2, err := client.GetStudents(context.Background(), &request2)
	if err != nil {
		fmt.Printf("get students failed: %s", err)
	} else {
		for _, response := range response2.Data {
			fmt.Println(response.Id)
		}
	}
	fmt.Println()

	//grpc基于http2协议，本身支持多路复用，N个goroutine用一个HTTP2连接没有任何问题，不会单纯因为没有可用连接而阻塞执行。
	const CLIENT_COUNT = 10 //10个client并发调用
	wg := sync.WaitGroup{}
	wg.Add(CLIENT_COUNT)
	for i := 0; i < CLIENT_COUNT; i++ {
		go func(i int) {
			defer wg.Done()
			request2 = student_service.StudentIDs{Ids: []int32{100, 300, 500, 700, 900, 1000}}
			fmt.Printf("client %d start\n", i)
			response2, err = client.GetStudents(context.Background(), &request2) //client支持多协程并发使用
			if err != nil {
				fmt.Printf("get students failed: %s", err)
				return
			}
			fmt.Printf("client %d finish\n", i)
		}(i)
	}
	wg.Wait()
	fmt.Println()

	//流式地接收response
	stream2, err := client.GetStudents2(context.Background(), &request2)
	if err != nil {
		fmt.Printf("build stream2 failed: %s", err)
	} else {
		for {
			resp, err := stream2.Recv() //从响应流中取得一个结果
			if err != nil {
				if err == io.EOF {
					break
				}
				fmt.Printf("recv response failed: %s\n", err)
				continue
			}
			fmt.Println(resp.Id)
		}
	}
	fmt.Println()

	//流式地发送request
	stream3, err := client.GetStudents3(context.Background())
	if err != nil {
		fmt.Printf("build stream3 failed: %s", err)
	} else {
		for i := 1; i < 5; i++ {
			request := student_service.StudentID{Id: int32(i)}
			stream3.Send(&request)
		}
		resp, err := stream3.CloseAndRecv() //关闭流，然后等待Server一次性返回全部结果
		if err != nil {
			fmt.Printf("recv response failed: %s", err)
		} else {
			for _, response := range resp.Data {
				fmt.Println(response.Id)
			}
		}
	}
	fmt.Println()

	//流式地发送request, 流式地接收response
	stream4, err := client.GetStudents4(context.Background())
	done := make(chan struct{})
	if err != nil {
		fmt.Printf("build stream4 failed: %s", err)
	} else {
		go func() { //发送和接收同时进行
			for {
				resp, err := stream4.Recv() //从响应流中取得一个结果
				if err != nil {
					if err == io.EOF {
						done <- struct{}{} //取出所有结果后对外发送一个信号
						break
					}
					fmt.Printf("recv response failed: %s\n", err)
					continue
				}
				fmt.Println(resp.Id)
			}
		}()
		for i := 1; i < 5; i++ {
			request := student_service.StudentID{Id: int32(i)}
			stream4.Send(&request)
		}
	}
	stream4.CloseSend() //关闭流。客户端创建的stream最终都要调close，server端不用调close
	<-done              //done之后往下走
	fmt.Println()
}

// go run .\micro_service\grpc\client\
